﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using Kent.Boogaart.HelperTrinity;
using WPFCABShell.Shell.Interface.Services.ToolBar;
using WPFCABShell.$safeprojectname$.Menu;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.ObjectBuilder;
using WPFCABShell.Shell.Interface;
using WPF = System.Windows.Controls;

namespace WPFCABShell.$safeprojectname$.ToolBar
{
	/// <summary>
	/// An implementation of <see cref="IToolBarService"/> for the WPF CAB shell.
	/// </summary>
	public sealed class ToolBarService : IToolBarService
	{
		private readonly WorkItem _workItem;
		private readonly IDictionary<string, WPF.ToolBar> _toolBars;

		public IEnumerable<string> ToolBarSiteNames
		{
			get
			{
				return _toolBars.Keys;
			}
		}

		public event EventHandler<ToolBarEventArgs> ToolBarAdded;

		public event EventHandler<ToolBarEventArgs> ToolBarRemoved;

		public event EventHandler<ToolBarEventArgs> ToolBarVisibilityChanged;

		[InjectionConstructor]
		public ToolBarService([Dependency(NotPresentBehavior=NotPresentBehavior.Throw)] WorkItem workItem)
		{
			_workItem = workItem;
			_toolBars = new Dictionary<string, WPF.ToolBar>();
		}

		public IToolBarButtonItem AddButtonItem(string toolBarSiteName, Interface.Services.Command.ICommand command, int? groupIndex, int? index)
		{
			ArgumentHelper.AssertNotNull(toolBarSiteName, "toolBarSiteName");
			ArgumentHelper.AssertNotNull(command, "command");
			ExceptionHelper.ThrowIf(!_workItem.UIExtensionSites.Contains(ExtensionSites.ToolBar.Name), "NoRootToolBarSite", ExtensionSites.ToolBar.Name);

			ToolBarButtonItem toolBarButtonItem = GetToolBarButtonItem(toolBarSiteName, command, groupIndex, index);

			if (!_workItem.UIExtensionSites.Contains(toolBarSiteName))
			{
				//auto-register a tool bar if an existing one cannot be found
				System.Windows.Controls.ToolBar toolBar = new System.Windows.Controls.ToolBar();
				_workItem.UIExtensionSites.RegisterSite(toolBarSiteName, toolBar);
				//add the tool bar into the root tool bar extension site
				_workItem.UIExtensionSites[ExtensionSites.ToolBar.Name].Add(toolBar);
				//cache the tool bar details
				_toolBars[toolBarSiteName] = toolBar;
				EventHelper.Raise(ToolBarAdded, this, new ToolBarEventArgs(toolBarSiteName));
			}

			//add the tool bar button item to the target site
			_workItem.UIExtensionSites[toolBarSiteName].Add(toolBarButtonItem);

			return toolBarButtonItem;
		}

		public void Remove(IToolBarItem item)
		{
			ArgumentHelper.AssertNotNull(item, "item");

			if (_workItem.UIExtensionSites.Contains(item.ToolBarSiteName))
			{
				//get the parent tool bar before we remove the item
				WPF.ToolBar toolBar = GetToolBarForItem(item);
				_workItem.UIExtensionSites[item.ToolBarSiteName].Remove(item);

				if (_workItem.UIExtensionSites[item.ToolBarSiteName].Count == 0)
				{
					//no items left in tool bar, so let's automatically remove it
					_workItem.UIExtensionSites.UnregisterSite(item.ToolBarSiteName);

					if (toolBar != null)
					{
						//remove the tool bar from the root tool bar extension site
						_workItem.UIExtensionSites[ExtensionSites.ToolBar.Name].Remove(toolBar);
					}

					//remove the information from the cache
					_toolBars.Remove(item.ToolBarSiteName);
					EventHelper.Raise(ToolBarRemoved, this, new ToolBarEventArgs(item.ToolBarSiteName));
				}
			}
		}

		public bool GetIsVisible(string toolBarSiteName)
		{
			ArgumentHelper.AssertNotNull(toolBarSiteName, "toolBarSiteName");
			ExceptionHelper.ThrowIf(!_toolBars.ContainsKey(toolBarSiteName), "ToolBarNotFound", toolBarSiteName);
			return _toolBars[toolBarSiteName].Visibility == Visibility.Visible;
		}

		public void SetIsVisible(string toolBarSiteName, bool isVisible)
		{
			ArgumentHelper.AssertNotNull(toolBarSiteName, "toolBarSiteName");
			ExceptionHelper.ThrowIf(!_toolBars.ContainsKey(toolBarSiteName), "ToolBarNotFound", toolBarSiteName);
			WPF.ToolBar toolBar = _toolBars[toolBarSiteName];
			Visibility visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;

			if (toolBar.Visibility != visibility)
			{
				toolBar.Visibility = visibility;
				EventHelper.Raise(ToolBarVisibilityChanged, this, new ToolBarEventArgs(toolBarSiteName));
			}
		}

		private ToolBarButtonItem GetToolBarButtonItem(string toolBarSiteName, Interface.Services.Command.ICommand command, int? groupIndex, int? index)
		{
			Debug.Assert(toolBarSiteName != null);
			Debug.Assert(command != null);

			ToolBarButtonItem toolBarButtonItem = new ToolBarButtonItem(toolBarSiteName, command);
			GroupingItemsControlUIAdapter.SetGroupIndex(toolBarButtonItem, groupIndex);
			GroupingItemsControlUIAdapter.SetIndex(toolBarButtonItem, index);

			return toolBarButtonItem;
		}

		private WPF.ToolBar GetToolBarForItem(IToolBarItem item)
		{
			Debug.Assert(item != null);
			WPF.ToolBar toolBar = null;
			_toolBars.TryGetValue(item.ToolBarSiteName, out toolBar);
			return toolBar;
		}

		private sealed class ToolBarButtonItem : Button, IToolBarButtonItem
		{
			private readonly string _toolBarSiteName;

			public string ToolBarSiteName
			{
				get
				{
					return _toolBarSiteName;
				}
			}

			public Interface.Services.Command.ICommand Command
			{
				get
				{
					return base.Command as Interface.Services.Command.ICommand;
				}
			}

			public ToolBarButtonItem(string toolBarSiteName, Interface.Services.Command.ICommand command)
			{
				Debug.Assert(toolBarSiteName != null);
				Debug.Assert(command != null);

				_toolBarSiteName = toolBarSiteName;
				base.Command = command;

				//bind content to command image and command name, converter prefers the image
				Image image = new Image();
				MultiBinding multiBinding = new MultiBinding();
				Binding binding = new Binding("Image");
				binding.Source = command;
				multiBinding.Bindings.Add(binding);
				binding = new Binding("Name");
				binding.Source = command;
				multiBinding.Bindings.Add(binding);
				multiBinding.Converter = ContentConverter.Instance;

				BindingOperations.SetBinding(this, Button.ContentProperty, multiBinding);

				//set up a dynamic reference to tool bar button style
				//this is a hack around the fact that styles are not inherited like they should be and
				//overriding DefaultResourceKey achieves squat
				SetResourceReference(FrameworkElement.StyleProperty, WPF.ToolBar.ButtonStyleKey);
			}

			private sealed class ContentConverter : IMultiValueConverter
			{
				public static readonly ContentConverter Instance = new ContentConverter();

				private ContentConverter()
				{
				}

				public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
				{
					Debug.Assert(values.Length == 2);
					ImageSource imageSource = values[0] as ImageSource;

					if (imageSource != null)
					{
						Image image = new Image();
						image.Source = imageSource;
						return image;
					}
					else
					{
						return values[1] as string;
					}
				}

				public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
				{
					throw new InvalidOperationException();
				}
			}
		}
	}
}
